#include "preHeader.h"
#include "QuestStorage.h"
#include "QuestMgr.h"
#include "Object/Player.h"

QuestStorage::QuestStorage(Player* pOwner)
: m_pOwner(pOwner)
, m_isLevelDirty(false)
{
}

QuestStorage::~QuestStorage()
{
	for (auto pQuestLog : m_allQuestLog) {
		delete pQuestLog;
	}
}

void QuestStorage::Update()
{
	if (m_isLevelDirty) {
		m_isLevelDirty = false;
		sQuestMgr.RefreshQuestWatchStatus4LevelDirty(m_pOwner);
	}
	if (!m_allQuestStatusDirty.empty()) {
		for (auto questTypeID : m_allQuestStatusDirty) {
			sQuestMgr.RefreshQuestWatchStatus4QuestStatusDirty(m_pOwner, questTypeID);
		}
		m_allQuestStatusDirty.clear();
	}
	if (!m_allChequeValueDirty.empty()) {
		for (auto chequeType : m_allChequeValueDirty) {
			sQuestMgr.RefreshQuestWatchStatus4ChequeValueDirty(m_pOwner, chequeType);
		}
		m_allChequeValueDirty.clear();
	}
	if (!m_allItemCountDirty.empty()) {
		for (auto itemTypeID : m_allItemCountDirty) {
			sQuestMgr.RefreshQuestWatchStatus4ItemCountDirty(m_pOwner, itemTypeID);
		}
		m_allItemCountDirty.clear();
	}

	if (!m_allAutoAcceptAvailQuests.empty()) {
		for (auto questTypeID : m_allAutoAcceptAvailQuests) {
			AutoAcceptQuestInstance(questTypeID);
		}
		m_allAutoAcceptAvailQuests.clear();
	}
	if (!m_allAutoSubmitAvailQuests.empty()) {
		for (auto questTypeID : m_allAutoSubmitAvailQuests) {
			AutoSubmitQuestInstance(questTypeID);
		}
		m_allAutoSubmitAvailQuests.clear();
	}
}

void QuestStorage::AddQuestLog(QuestLog* pQuestLog)
{
	m_allQuestLog.push_back(pQuestLog);
}

void QuestStorage::RemoveQuestLog(QuestLog* pQuestLog)
{
	auto endItr = std::remove(
		m_allQuestLog.begin(), m_allQuestLog.end(), pQuestLog);
	if (endItr != m_allQuestLog.end()) {
		m_allQuestLog.erase(endItr, m_allQuestLog.end());
	}
}

void QuestStorage::SetLevelDirty()
{
	m_isLevelDirty = true;
}

void QuestStorage::SetQuestStatusDirty(uint32 questTypeID)
{
	m_allQuestStatusDirty.insert(questTypeID);
}

void QuestStorage::SetChequeValueDirty(ChequeType chequeType)
{
	m_allChequeValueDirty.insert(chequeType);
}

void QuestStorage::SetItemCountDirty(uint32 itemTypeID)
{
	m_allItemCountDirty.insert(itemTypeID);
}

void QuestStorage::EventCleanRepeatQuests(QuestRepeatType questRepeatType)
{
	m_finishedRepeatQuests[(int)questRepeatType].clear();
}

void QuestStorage::SetQuestFinished(uint32 questTypeID)
{
	for (auto pQuestLog : m_allQuestLog) {
		if (pQuestLog->GetQuestTypeID() != questTypeID) {
			continue;
		}
		if (!pQuestLog->SetQuestFinished()) {
			continue;
		}
		pQuestLog->SendUpdateQuestStatus();
		pQuestLog->RefreshQuestFinishStatus();
	}
}

void QuestStorage::TryTriggerEvent(
	QuestConditionType type, const std::function<bool(QuestLog*)>& func)
{
	for (auto pQuestLog : m_allQuestLog) {
		if (!pQuestLog->IsQuestCare(type)) {
			continue;
		}
		if (!func(pQuestLog)) {
			continue;
		}
		pQuestLog->SendUpdateQuestStatus();
		pQuestLog->RefreshQuestFinishStatus();
	}
}

void QuestStorage::OnTalkNPC(Creature* pCreature)
{
	TryTriggerEvent(QuestConditionType::TalkNPC, [=](QuestLog* pQuestLog) {
		return pQuestLog->OnTalkNPC(pCreature);
	});
}

void QuestStorage::OnKillCreature(Creature* pCreature)
{
	TryTriggerEvent(QuestConditionType::KillCreature, [=](QuestLog* pQuestLog) {
		return pQuestLog->OnKillCreature(pCreature);
	});
}

void QuestStorage::OnHaveCheque(ChequeType chequeType, uint64 chequeValue, CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams)
{
	TryTriggerEvent(QuestConditionType::HaveCheque, [=](QuestLog* pQuestLog) {
		return pQuestLog->OnHaveCheque(chequeType, chequeValue, flowType, flowParams);
	});
}

void QuestStorage::OnHaveItem(uint32 itemTypeID, uint32 itemCount, ITEM_FLOW_TYPE flowType, params<uint32> flowParams)
{
	TryTriggerEvent(QuestConditionType::HaveItem, [=](QuestLog* pQuestLog) {
		return pQuestLog->OnHaveItem(itemTypeID, itemCount, flowType, flowParams);
	});
}

void QuestStorage::OnUseItem(uint32 itemTypeID, uint32 itemCount, uint32 actionUniqueKey)
{
	TryTriggerEvent(QuestConditionType::UseItem, [=](QuestLog* pQuestLog) {
		return pQuestLog->OnUseItem(itemTypeID, itemCount, actionUniqueKey);
	});
}

void QuestStorage::OnPlayStory(uint32 questTypeID, bool isSucc)
{
	TryTriggerEvent(QuestConditionType::PlayStory, [=](QuestLog* pQuestLog) {
		return pQuestLog->GetQuestTypeID() == questTypeID &&
			pQuestLog->OnPlayStory(isSucc);
	});
}

void QuestStorage::AddToFinishedQuest(const QuestPrototype* pQuestProto)
{
	if (pQuestProto->questFlags.isCantArchive) {
		return;
	}
	switch (QuestRepeatType(pQuestProto->questRepeatType)) {
	case QuestRepeatType::None:
		m_finishedQuests.insert(pQuestProto->questTypeID);
		break;
	case QuestRepeatType::Daily:
	case QuestRepeatType::Weekly:
	case QuestRepeatType::Monthly:
	case QuestRepeatType::Forever:
		m_finishedRepeatQuests[pQuestProto->questRepeatType]
			[pQuestProto->questTypeID] += 1;
		break;
	}
}

bool QuestStorage::IsQuestFinished(const QuestPrototype* pQuestProto) const
{
	switch (QuestRepeatType(pQuestProto->questRepeatType)) {
	case QuestRepeatType::None:
		return m_finishedQuests.count(pQuestProto->questTypeID) != 0;
	case QuestRepeatType::Daily:
	case QuestRepeatType::Weekly:
	case QuestRepeatType::Monthly:
	case QuestRepeatType::Forever:
		return pQuestProto->questRepeatMax != 0 ?
			IsQuestFinished4Repeatable(pQuestProto) : false;
	}
	return false;
}

bool QuestStorage::IsQuestFinished4Repeatable(const QuestPrototype* pQuestProto) const
{
	auto frQuests = m_finishedRepeatQuests[pQuestProto->questRepeatType];
	auto itr = frQuests.find(pQuestProto->questTypeID);
	return itr != frQuests.end() ?
		itr->second < pQuestProto->questRepeatMax : false;
}

bool QuestStorage::IsQuestHasFinished(const QuestPrototype* pQuestProto) const
{
	switch (QuestRepeatType(pQuestProto->questRepeatType)) {
	case QuestRepeatType::None:
		return m_finishedQuests.count(pQuestProto->questTypeID) != 0;
	case QuestRepeatType::Daily:
	case QuestRepeatType::Weekly:
	case QuestRepeatType::Monthly:
	case QuestRepeatType::Forever:
		return m_finishedRepeatQuests[pQuestProto->questRepeatType].
			count(pQuestProto->questTypeID) != 0;
	}
	return false;
}

bool QuestStorage::IsQuestHasFinished(uint32 questTypeID) const
{
	return IsQuestHasFinished(GetDBEntry<QuestPrototype>(questTypeID));
}

QuestLog* QuestStorage::GetQuestLogByKey(uint32 questUniqueKey) const
{
	for (auto pQuestLog : m_allQuestLog) {
		if (pQuestLog->GetQuestGuid() == questUniqueKey) {
			return pQuestLog;
		}
	}
	return NULL;
}

QuestLog* QuestStorage::GetQuestLogByEntry(uint32 questTypeID) const
{
	for (auto pQuestLog : m_allQuestLog) {
		if (pQuestLog->GetQuestTypeID() == questTypeID) {
			return pQuestLog;
		}
	}
	return NULL;
}

uint32 QuestStorage::GetQuestLackItemCount(uint32 questTypeID, uint32 itemTypeID) const
{
	uint32 lackItemCount = 0;
	for (auto pQuestLog : m_allQuestLog) {
		if (pQuestLog->GetQuestTypeID() == questTypeID) {
			lackItemCount = std::max(lackItemCount,
				pQuestLog->GetQuestLackItemCount(itemTypeID));
		}
	}
	return lackItemCount;
}

void QuestStorage::PushQuestWatchStatus(const QuestPrototype* pQuestProto, QUEST_STATUS questStatus)
{
	if (pQuestProto->questFlags.isAutoAccept) {
		if (questStatus == QMGR_QUEST_AVAILABLE) {
			m_allAutoAcceptAvailQuests.insert(pQuestProto->questTypeID);
		}
	}

	bool isDirty = false;
	bool isCare = IsCareQuestStatus(questStatus);
	auto itr = m_allQuestWatchStatus.find(pQuestProto->questTypeID);
	if (itr != m_allQuestWatchStatus.end()) {
		if (isCare) {
			if (itr->second != questStatus) {
				itr->second = questStatus;
				isDirty = true;
			}
		} else {
			m_allQuestWatchStatus.erase(itr);
			isDirty = true;
		}
	} else {
		if (isCare) {
			m_allQuestWatchStatus.emplace(pQuestProto->questTypeID, questStatus);
			isDirty = true;
		}
	}

	if (isDirty) {
		NetPacket pack(SMSG_UPDATE_QUEST_WATCH_STATUS);
		pack << pQuestProto->questTypeID << (int8)questStatus;
		m_pOwner->SendPacket(pack);
	}
}

void QuestStorage::PushQuestFinishStatus(QuestLog* pQuestLog)
{
	if (pQuestLog->GetQuestProto()->questFlags.isAutoSubmit) {
		m_allAutoSubmitAvailQuests.insert(pQuestLog->GetQuestGuid());
	}
}

void QuestStorage::AutoAcceptQuestInstance(uint32 questTypeID) const
{
	sQuestMgr.HandleAcceptQuest(m_pOwner, NULL, GetDBEntry<QuestPrototype>(questTypeID));
}

void QuestStorage::AutoSubmitQuestInstance(uint32 questUniqueKey) const
{
	sQuestMgr.HandleSubmitQuest(m_pOwner, NULL, questUniqueKey);
}

void QuestStorage::PostInitAndUpdateQuests()
{
	for (auto pQuestLog : m_allQuestLog) {
		pQuestLog->PostInit();
		pQuestLog->SendUpdateQuestStatus();
		pQuestLog->RefreshQuestFinishStatus();
	}
}

void QuestStorage::BuildCreatePacketForPlayer(INetPacket& pck, Player* pPlayer)
{
	pck << (u16)m_allQuestLog.size();
	for (auto pQuestLog : m_allQuestLog) {
		pQuestLog->GetQuestProp().Save(pck);
	}
}

std::string QuestStorage::SaveQuests() const
{
	TextPacker packer;
	for (auto pQuestLog : m_allQuestLog) {
		pQuestLog->GetQuestProp().Save(packer);
		packer.PutDelimiter(';');
	}
	return packer.str();
}

void QuestStorage::LoadQuests(const std::string& data)
{
	TextUnpacker unpacker(data.c_str());
	while (!unpacker.IsEmpty()) {
		inst_quest_prop questProp;
		questProp.Load(unpacker);
		auto pQuestProto = GetDBEntry<QuestPrototype>(questProp.questTypeID);
		if (pQuestProto != NULL) {
			auto pQuestLog = new QuestLog(m_pOwner);
			pQuestLog->Load(pQuestProto, questProp);
			AddQuestLog(pQuestLog);
		}
	}
}

std::string QuestStorage::SaveQuestsDone() const
{
	TextPacker packer;
	auto deflateQuests = DeflateFinishedQuests();
	for (auto questTypeID : m_finishedQuests) {
		auto itr = deflateQuests.find(questTypeID);
		if (itr == deflateQuests.end() || !itr->second) {
			packer << questTypeID;
		}
	}
	packer.PutDelimiter(';');
	for (auto& finishedRepeatQuests : m_finishedRepeatQuests) {
		for (auto& pair : finishedRepeatQuests) {
			packer << pair.first << pair.second;
		}
		packer.PutDelimiter(';');
	}
	return packer.str();
}

void QuestStorage::LoadQuestsDone(const std::string& data)
{
	TextUnpacker unpacker(data.c_str());
	while (!unpacker.IsEmpty() && !unpacker.IsDelimiter(';')) {
		m_finishedQuests.insert(unpacker.Unpack<uint32>());
	}
	InflateFinishedQuests();
	for (auto& finishedRepeatQuests : m_finishedRepeatQuests) {
		if (!unpacker.IsEmpty()) {
			do {
				std::pair<uint32, size_t> pair;
				unpacker >> pair.first >> pair.second;
				finishedRepeatQuests.insert(pair);
			} while (!unpacker.IsEmpty() && !unpacker.IsDelimiter(';'));
		}
	}
}

std::unordered_map<uint32, bool> QuestStorage::DeflateFinishedQuests() const
{
	std::unordered_map<uint32, bool> deflateQuests;
	deflateQuests.reserve(m_finishedQuests.size());
	auto pQuestTbl = sDBMgr.GetTable<QuestPrototype>();
	for (auto questTypeID : m_finishedQuests) {
		auto pQuestProto = pQuestTbl->GetEntry(questTypeID);
		if (pQuestProto == NULL) {
			deflateQuests[questTypeID] = true;
			continue;
		}
		if (pQuestProto->questReqQuests.empty()) {
			continue;
		}
		if (pQuestProto->questReqQuests.size() > 1 &&
			pQuestProto->questFlags.isAnyReqQuest) {
			for (auto questReqQuest : pQuestProto->questReqQuests) {
				if (m_finishedQuests.count(questReqQuest) != 0) {
					deflateQuests[questTypeID] = false;
				}
			}
		} else {
			for (auto questReqQuest : pQuestProto->questReqQuests) {
				deflateQuests.emplace(questReqQuest, true);
			}
		}
	}
	return deflateQuests;
}

void QuestStorage::InflateFinishedQuests()
{
	std::queue<uint32> inflateQueueQuests;
	for (auto questTypeID : m_finishedQuests) {
		inflateQueueQuests.push(questTypeID);
	}
	auto pQuestTbl = sDBMgr.GetTable<QuestPrototype>();
	for (; !inflateQueueQuests.empty(); inflateQueueQuests.pop()) {
		auto questTypeID = inflateQueueQuests.front();
		auto pQuestProto = pQuestTbl->GetEntry(questTypeID);
		if (pQuestProto == NULL) {
			continue;
		}
		if (pQuestProto->questReqQuests.empty()) {
			continue;
		}
		if (pQuestProto->questReqQuests.size() > 1 &&
			pQuestProto->questFlags.isAnyReqQuest) {
			for (auto questReqQuest : pQuestProto->questReqQuests) {
				if (m_finishedQuests.count(questReqQuest) != 0) {
					continue;
				}
			}
			auto questReqQuest = pQuestProto->questReqQuests.front();
			if (m_finishedQuests.insert(questReqQuest).second) {
				inflateQueueQuests.push(questReqQuest);
			}
		} else {
			for (auto questReqQuest : pQuestProto->questReqQuests) {
				if (m_finishedQuests.insert(questReqQuest).second) {
					inflateQueueQuests.push(questReqQuest);
				}
			}
		}
	}
}
